1 module hip.game2d.tileworld; 2 public import hip.api.data.tilemap; 3 public import hip.component.physics; 4 public import hip.math.collision; 5 import hip.util.algorithm; 6 7 8 9 class TileWorld 10 { 11 IHipTilemap map; 12 ///Affects position 13 float constantGravity = 0; 14 ///Affects velocity 15 float gravity = 0; 16 17 BodyRectComponent[] dynamicBodies; 18 HipTileLayer[] collidibleLayers; 19 20 this(IHipTilemap map, float gravity = 0, float constantGravity = 0) 21 { 22 this.map = map; 23 this.gravity = gravity; 24 this.constantGravity = constantGravity; 25 } 26 27 private Rect[128] rectCache; 28 Rect[] getRectsOverlapping(HipTileLayer layer, in Rect input) @nogc 29 { 30 import hip.math.utils; 31 if(!layer.isInLayerBoundaries( 32 cast(int)input.x, cast(int)input.y, cast(int)input.w, cast(int)input.h 33 )) 34 return rectCache[0..0]; 35 36 uint tileW = map.tileWidth, tileH = map.tileHeight; 37 float x = input.x - layer.x; 38 float y = input.y - layer.y; 39 40 41 42 43 44 float inputTilesWidth = input.w / tileW; 45 float inputTilesHeight = input.h / tileH; 46 47 float i = x/tileW; 48 float j = y/tileH; 49 float i2 = ceil(min(i + inputTilesWidth, layer.width)); 50 float j2 = ceil(min(j + inputTilesHeight, layer.height - 1)); 51 52 53 float plusI = min(inputTilesWidth, 1); 54 float plusJ = min(inputTilesHeight, 1); 55 56 int lastI = -1; 57 int lastJ = -1; 58 59 int rects = 0; 60 for(; j < j2; j+= plusJ) 61 for(float _i = i; _i < i2; _i+= plusI) 62 { 63 if(_i < 0 || j < 0) 64 continue; 65 int ui = cast(int)_i; 66 int uj = cast(int)j; 67 68 if(ui == lastI && uj == lastJ) 69 continue; 70 lastI = ui; 71 lastJ = uj; 72 73 ushort tID = layer.checkedGetTile(ui, uj); 74 if(tID != 0) 75 { 76 rectCache[rects++] = Rect(layer.x + ui*tileW, layer.y + uj*tileH, tileW, tileH); 77 } 78 } 79 return rectCache[0..rects]; 80 } 81 82 void addDynamic(IComponentizable component) 83 { 84 auto comp = component.getComponent!BodyRectComponent; 85 assert(comp !is null, "No BodyRectComponent found. Ensure that you have done mixin IncludeComponents!(BodyRectComponent) and you had called initializeComponents() somewhere"); 86 assert(comp.size.w != 0, "Can't have 0 width component"); 87 assert(comp.size.h != 0, "Can't have 0 height component"); 88 dynamicBodies~= comp; 89 } 90 91 void addCollidibleLayer(HipTileLayer layer) 92 { 93 collidibleLayers~= layer; 94 } 95 96 void update(float dt) @nogc 97 { 98 import hip.util.algorithm:quicksort; 99 struct DynamicRectCollision 100 { 101 Vector2 normal; 102 float time; 103 } 104 __gshared DynamicRectCollision[128] colCache; 105 foreach(dynBody; dynamicBodies) 106 { 107 dynBody.velocity.y += gravity; 108 Rect bodyRec = dynBody.rect; 109 int z = 0; 110 111 foreach(HipTileLayer l; collidibleLayers) 112 for(int j = 0; j < l.height; j++) 113 for(int i = 0; i < l.width; i++) 114 { 115 if(l.getTile(i, j) != 0) 116 { 117 DynamicRectCollision col = void; 118 if(isDynamicRectOverlappingRect(bodyRec, dynBody.velocity, Rect(l.x + i*l.tileWidth, l.y+j*l.tileHeight, l.tileWidth, l.tileHeight), dt, col.normal, col.time)) 119 colCache[z++] = col; 120 } 121 } 122 if(z > 0) 123 foreach(col; quicksort(colCache[0..z], ((DynamicRectCollision a, DynamicRectCollision b) => a.time < b.time))) 124 resolveDynamicRectOverlappingRect(col.normal, dynBody.velocity, col.time); 125 dynBody.position+= dynBody.velocity* dt; 126 } 127 } 128 }